<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
  <link rel="Stylesheet" type="text/css" href=
  "../../default.css" />
  <meta http-equiv="Content-Type" content=
  "text/html; charset=utf-8" />

  <title>Формат серийного номера</title>
  <style type="text/css">
/*<![CDATA[*/
                th {text-align:left;}
                table {border-collapse:collapse; margin-top: 4px;}
                td,th {border: 1px solid #B0B0B0; padding-left:10;padding-right:10;}
    /*]]>*/
  </style>
</head>

<body>
  <h1>Формат серийного номера</h1>

  <h3>Структура серийного номера</h3>

  <p>Серийный номер состоит из блоков. Каждый блок
  начинается с байта идентификатора, который определяет его
  содержимое и, возможно, длину. Последним всегда идет блок с
  идентификатором 255, содержащий контрольную сумму всего серийного
  номера, за исключением последнего блока.</p>

  <p>В зависимости от типа блока, он может иметь фиксированную или
  переменную длину. Во втором случае обычно длина идет в следующем
  за идентификатором блока байте. Подробно формат каждого блока
  описан ниже.</p>

  <h3>Формат блоков</h3>

  <table border="1" cellspacing="0" cellpadding="2">
    <tr>
      <th>ID</th>

      <th>Название</th>

      <th>Размер (байт)</th>

      <th>Описание</th>

      <th>Пример</th>
    </tr>

    <tr>
      <td>0x01</td>

      <td>Версия</td>

      <td>1</td>

      <td>Блок содержит версию серийного номера размером в 1 байт.
      Версия должна быть равна 1.</td>

      <td>01 01</td>
    </tr>

    <tr>
      <td>0x02</td>

      <td>Имя пользователя</td>

      <td>1 + N</td>

      <td>Блок содержит имя пользователя в кодировке UTF-8. Перед
      именем пользователя располагается 1 байт длины имени. Таким
      образом, длина имени пользователя не может превышать 255
      байт. Завершающий 0 не требуется.</td>

      <td>02 04 4A 5F 48 4E</td>
    </tr>

    <tr>
      <td>0x03</td>

      <td>E-Mail</td>

      <td>1 + N</td>

      <td>Блок содержит e-mail пользователя в кодировке UTF-8.
      Перед e-mail'ом располагается 1 байт длины. Таким образом,
      длина e-mail не может превышать 255 байт. Завершающий 0 не
      требуется.</td>

      <td>03 07 61 40 62 2E 63 6F 6D</td>
    </tr>

    <tr>
      <td>0x04</td>

      <td>Идентификатор оборудования</td>

      <td>1 + N</td>

      <td>Блок содержит идентификатор оборудования, возвращенный
      функцией <strong>VMProtectGetCurrentHWID()</strong>. Функция
      возвращает строку в кодировке base-64, в серийный номер
      помещается раскодированный вариант строки. Длина блока данных
      должна быть кратна 4. Перед блоком данных располагается байт
      длины. Максимальная длина блока - 32 байта.</td>

      <td>04 08 E1 E2 E3 E4 A1 A2 A3 A4</td>
    </tr>

    <tr>
      <td>0x05</td>

      <td>Дата окончания лицензии</td>

      <td>4</td>

      <td>Блок содержит дату окончания действия серийного номера.
      Формат хранения даты описан ниже.</td>

      <td>05 01 0A 07 DA</td>
    </tr>

    <tr>
      <td>0x06</td>

      <td>Максимальное время работы</td>

      <td>1</td>

      <td>Блок содержит 1 байт с временем работы программы в
      минутах. Максимальное время работы, таким образом, может
      составлять 255 минут.</td>

      <td>06 05</td>
    </tr>

    <tr>
      <td>0x07</td>

      <td>Код продукта</td>

      <td>8</td>

      <td>Блок содержит код продукта - 8 байт, которые создаются
      VMProtect и выгружаются при экспорте параметров продукта.
      Выгружаются они в кодировке base-64, перед помещением в
      серийный номер строку необходимо преобразовать в массив байт.
      Размер массива должен быть строго равен 8 байтам.
      <strong>Этот блок является обязательным, без него защищенная
      программа будет работать некорректно!</strong></td>

      <td>07 01 02 03 04 05 06 07 08</td>
    </tr>

    <tr>
      <td>0x08</td>

      <td>Данные пользователя</td>

      <td>1 + N</td>

      <td>Блок содержит до 255 байт пользовательских данных.
      Система лицензирования не анализирует эти данные и возвращает
      их при вызове функции
      <strong>VMProtectGetSerialNumberData()</strong>. Перед блоком
      данных располагается байт, содержащий размер пользовательских
      данных.</td>

      <td>08 05 01 02 03 04 05</td>
    </tr>

    <tr>
      <td>0x09</td>

      <td>Максимальная дата сборки</td>

      <td>4</td>

      <td>Блок содержит максимальную дату сборки приложения, с
      которым будет работать этот серийный номер. Формат хранения
      даты описан ниже.</td>

      <td>09 01 0A 07 DA</td>
    </tr>

    <tr>
      <td>0xFF</td>

      <td>Контрольная сумма</td>

      <td>4</td>

      <td>Блок содержит контрольную сумму серийного номера. Блок
      располагается последним и контрольная сумма считается для
      всех блоков, идущих перед этим. Подробнее о расчете
      контрольной суммы написано ниже.</td>

      <td>FF 01 02 03 04</td>
    </tr>
  </table>

  <h3>Формат хранения даты</h3>

  <p>Даты в серийном номере хранятся в виде двойного слова -
  0xYYYYMMDD. Старшее слово содержит год, а младшее - день и месяц.
  Байты расположены в порядке Little Endian - от младшего к
  старшему. В случае, если имеется указатель на первый байт записи,
  то дата может быть считана и записана следующим кодом:</p>
  <pre class="code"><strong>byte *</strong>pDate = 0xNNNNNN; // адрес даты
<strong>*</strong>(<strong>DWORD *</strong>)pDate = (2010 &lt;&lt; 16) | (10 &lt;&lt; 8) | 1; // October 1, 2010
<strong>DWORD</strong> dwExp = *(<strong>DWORD *</strong>)pDate;
</pre>

  <h3>Подсчет контрольной суммы</h3>

  <p>Контрольная сумма серийного номера вычисляется при помощи
  алгоритма хеширования SHA-1. Результатом работы алгоритма
  являются пять 32-битных слов. Первое слово используется в
  качестве контрольной суммы серийного номера. <strong>Обратите
  внимание:</strong> слово записывается в формате Little Endian (от
  младшего байта к старшему). В строковом представлении хеша SHA-1
  используется формат Big Endian - числа записываются от старшего
  байта к младшему, поэтому если SHA-1 генерируется функцией,
  которая возвращает строку (как в PHP), то первые четыре байта
  хеша необходимо предварительно
  развернуть.</p>

  <h3>Дополнительная информация</h3>

  <p>Блоки с номерами, отличными от описанных выше, игнорируются
  системой лицензирования. Возможно появление новых блоков в
  будущих версиях. <strong>Не рекомендуется создавать свои блоки,
  используя свободные идентификаторы!</strong> Во-первых, это может
  привести к неработоспособности ключа с новыми версиями системы
  лицензирования. А во-вторых, защищаемая программа все равно не
  сможет прочитать значения этих блоков. Для хранения какой-либо
  информации в ключе нужно использовать поле <strong>User
  Data</strong>, которое предназначено именно для этого.</p>

  <p>Серийный номер не содержит "соли" (SALT) - случайной
  информации, предназначенной для того, чтобы ключи для одних и тех
  же данных получались разными. Эта задача возлагается на алгоритм
  шифрования. Если необходимы отличия на уровне серийных номеров,
  скажем для серии ключей, проданных в организацию, можно записать
  номер ключа строкой в поле имени пользователя (ООО "Компания",
  ключ 1 из 10) или занести эту информацию в поле <strong>User
  Data</strong> в любом удобном для разработчика формате.</p><br />
  <br />
  <br />
  <br />
  <br />
  <hr noshade="noshade" size="1" />

  <div align="center">
    © 2006-2015 Copyright VMProtect Software
  </div>
</body>
</html>
